####################
# Ruyk
# Adding support for OpenAcc 3.0


#from yacf.Frontend.C99 import c_parser, c_ast


from yacf.Frontend.Acc.plyparser import PLYParser, Coord, ParseError

from yacf.Frontend.Acc.acc_lexer import AccLexer

from yacf.Frontend.Acc import acc_ast

# TODO: THIS NODE SHOULD NOT USE PRAGMA node at all
from yacf.Frontend.C99 import c99_ast as c_ast, c99_ast

class AccParser(PLYParser):

    def p_pp_directive_1(self, p):
       """ pp_directive  : PPHASH pragma_directive 
       """
       p[0] = p[2]

    def p_pragma_directive(self, p):
       """ pragma_directive : PRAGMA ACC directive"""
       p[0] = [c_ast.Pragma(
            name='acc', 
            stmt = p[3],
            coord=self._coord(p.lineno(1)))]

    def p_pragma_directive_2(self, p):
       """ pragma_directive : PRAGMA ACC acc_directive"""
       p[0] = [c_ast.Pragma(
            name='acc', 
            stmt = p[3],
            coord=self._coord(p.lineno(1)))]


    def p_external_declaration_5(self, p):
        """ external_declaration : ACCIGNORE """
        print 'En accignore ******************'
        p[0] =  [acc_ast.AccIgnore(name = "AccIgnore", code = p[1])]


    def p_statement_1(self, p):
        """ statement : ACCIGNORE
        """
        p[0] =  [acc_ast.AccIgnore(name = "AccIgnore", code = p[1])]

       

    # --acc yacf-frangollo ------------------------------
    def p_acc_region_type(self, p):
        """ acc_region_type : PARALLEL 
                            | KERNELS
                            | DATA
        """
        p[0] = p[1]


    def p_acc_directive_9(self, p):
        """ acc_directive : HOSTDATA USEDEVICE LPAREN identifier_list RPAREN statement
        """
        p[0] = acc_ast.AccHostData(name = p[1], varlist = p[4].params, stmt = c99_ast.Compound(block_items = [p[6],]) )

    def p_acc_directive_8(self, p):
        """ acc_directive : acc_region_type acc_clause_list statement """
        if p[1].lower() == 'parallel':
           p[0] = acc_ast.AccParallel(name = p[1], stmt = p[3], clauses = p[2] or [], coord=self._coord(p.lineno(1)))     
        elif p[1].lower() == 'kernels':
           p[0] = acc_ast.AccKernels(name = p[1], stmt = p[3], clauses = p[2] or [], coord=self._coord(p.lineno(1)))     
        elif p[1].lower() == 'data':
           p[0] = acc_ast.AccData(name = p[1], stmt = p[3], clauses = p[2] or [], coord=self._coord(p.lineno(1)))     

        else:
           print p[1]
           raise ParseError()


    def p_acc_directive_11(self, p):
        """ acc_directive : workshare_directive """
        p[0] = p[1]

    def p_acc_size_expression(self, p):
        """ acc_size_expression : LBRACKET expression RBRACKET
                                | LBRACKET expression COLON expression RBRACKET
                                | LBRACKET expression RBRACKET LBRACE identifier RBRACE
        """
        if p[3] == ':':
            print " *** colon "
            p[0] = acc_ast.AccSizeExpression(start = p[2], end = p[4], size = None, stride = None)
        else:
            if len(p) == 7: # Has a stride argument
               p[0] = acc_ast.AccSizeExpression(start = p[2], end = None, size = None, stride = p[5])
            elif len(p) > 4:
               p[0] = acc_ast.AccSizeExpression(start = p[2], end = None, size = p[4], stride = None)
            else:
               p[0] = acc_ast.AccSizeExpression(start = p[2], end = None, size = None, stride = None)


    def p_acc_size_expression_list(self, p):
        """ acc_size_expression_list : acc_size_expression  acc_size_expression 
                                     | acc_size_expression 
        """
        if len(p) > 2:
            p[0] = (p[1],p[2])
        else:
            p[0] = p[1]


    def p_acc_complex_identifier_list(self, p):
        """ acc_complex_identifier_list : identifier 
                                        | identifier acc_size_expression_list
                                        | acc_complex_identifier_list COMMA identifier
                                        | acc_complex_identifier_list COMMA identifier acc_size_expression_list
        """
#        acc_id = p[1]
        definitions = {}
        if len(p) == 3:
           definitions[p[1]] = p[2]
           p[0] = acc_ast.AccParamList([p[1]], definitions, p[1].coord)
        elif len(p) == 4:
           p[1].params.append(p[3])
           p[0] = p[1]
        elif len(p) == 5:
           definitions[p[3]] = p[4]
           p[1].params.append(p[3])
           p[1].definitions.update(definitions)
           p[0] = p[1]
        else:
            p[0] = acc_ast.AccParamList([p[1]], definitions, p[1].coord)

    def p_acc_clause_1(self, p):
        """ acc_clause : NAME LPAREN STRING_LITERAL RPAREN 
                       | acc_complex_clause  LPAREN acc_complex_identifier_list RPAREN 
                       | REDUCTION LPAREN reduction_operator  COLON identifier_list RPAREN 
        """
        tmp = None
        additional = None
        if p[1] == 'name':
            # Remove semicolons fron STRING LITERAL
            tmp = acc_ast.AccName(name = 'NAME', label = p[3][1:-1], coord = self._coord(p.lineno(1)))    
        elif p[1] == 'reduction':
            tmp = acc_ast.AccClause(type = p[3] , name = 'REDUCTION', identifiers = p[5], coord = self._coord(p.lineno(1)))
        else:
            tmp = acc_ast.AccCopy(name = p[1].upper(), identifiers = p[3], present = (p[1][0] == 'p'), coord = self._coord(p.lineno(1)));

        p[0] = []
        p[0].append(tmp)
        if additional:
            p[0].extend(additional)

    def p_acc_clause_4(self, p):
        """ acc_clause : NUM_WORKERS LPAREN constant_expression RPAREN
                       | NUM_GANGS LPAREN constant_expression RPAREN
                       | VECTOR_LENGTH LPAREN constant_expression RPAREN
                       | GANG LPAREN constant_expression RPAREN
                       | WORKER LPAREN constant_expression RPAREN
                       | VECTOR LPAREN constant_expression RPAREN
        """
        p[0] = [acc_ast.AccParallelOption(name=p[1], value=p[3])]



    def p_acc_complex_clause(self, p):
        """ acc_complex_clause : COPYIN
                               | COPYOUT
                               | COPY
                               | PCOPY
                               | PCOPYIN
                               | PCOPYOUT
                               | CREATE
                               | PCREATE
			       | PRIVATE
			       | FIRSTPRIVATE
        """
        # print " Complex clause " + str(p[1])
        p[0] = p[1]
        
    def p_acc_clause_2(self, p):
        """ acc_clause : empty """
        p[0] = []


    def p_acc_iteration_statement(self, p):
        """ acc_iteration_statement : PPHASH PRAGMA ACC acc_directive 
                                    | iteration_statement
        """
        if len(p) == 5:
           p[0] = p[4]
        else:
           p[0] = p[1]


    def p_workshare_directive_1(self, p):
       """ workshare_directive : LOOP acc_clause_list acc_iteration_statement """
       # Start of a parallel region within a for
       p[0] = acc_ast.AccLoop(name=p[1].lower(), clauses = p[2], stmt = p[3], coord = self._coord(p.lineno(1)))

    def p_workshare_directive_2(self, p):
       """ workshare_directive : LOOP  acc_iteration_statement """
       # Start of a parallel region within a for
       p[0] = acc_ast.AccLoop(name=p[1].lower(), clauses = [], stmt = p[2], coord = self._coord(p.lineno(1)))


    def p_directive_2(self, p):
       """ directive : acc_region_type LOOP acc_clause_list acc_iteration_statement
                     | acc_region_type LOOP acc_iteration_statement """
       print " Region + Loop "
       if p[1].lower() == 'data':
            raise ParseError("#pragma data loop is not allowed")

       clauses = None
       stmt = p[3]
       if (len(p) == 5):
         clauses = p[3]
         stmt = p[4]
       else:
	 clauses = []
         stmt = p[3]

       regionDict = { 'parallel' : acc_ast.AccParallel, 'kernels' : acc_ast.AccKernels}
       non_copy = []
       copy = []
       # TODO: What to do with deviceptr?
       if p[3] and not stmt == p[3]:
           non_copy = [ c for c in p[3] if not isinstance(p[3], acc_ast.AccCopy)]
           copy     = [ c for c in p[3] if     isinstance(p[3], acc_ast.AccCopy)]

       acc_loop = acc_ast.AccLoop(name=p[2].lower(), clauses = non_copy, stmt = stmt, coord = self._coord(p.lineno(1)))
       p[0] = regionDict[p[1].lower()](name = p[1], stmt = c99_ast.Compound(block_items = [acc_loop]), clauses = copy , coord=self._coord(p.lineno(1)))     

    def p_directive_3(self,p):
       """ directive : acc_region_type compound_statement """
       # Start of a parallel region
       p[0] = acc_ast.AccParallel(name='parallel', clauses = None, stmt = p[2], coord = self._coord(p.lineno(1)))

    def p_directive_4(self, p):
       """ directive : workshare_directive """
       # We are on a parallel region
       p[0] = p[1]



    def p_directive_8(self, p):
       """ directive : UPDATE  acc_update_destination LPAREN identifier_list RPAREN 
                     | WAIT LPAREN constant_expression RPAREN
                     | WAIT 
       """
       if p[1].lower() == 'update':
           p[0] = acc_ast.AccUpdate(name = "update", destination = p[2], identifiers = p[4].params, coord = self._coord(p.lineno(1)))
       else:
           if len(p) > 2:
               p[0] = acc_ast.AccWait(name = "wait", value  = p[3], coord = self._coord(p.lineno(1)))
           else:
               p[0] = acc_ast.AccWait(name = "wait", value  = None, coord = self._coord(p.lineno(1)))



    def p_acc_update_destination(self, p):
       """ acc_update_destination : HOST
                                  | DEVICE
       """
       p[0] = p[1].lower();


    def p_acc_clause_list(self, p):
       """ acc_clause_list : acc_clause_list acc_clause
			   | acc_clause_list COMMA acc_clause
                           | acc_clause """
       if len(p) == 4:
           p[0] = p[1] + p[3]
       else:
           p[0] = p[1] if len(p) == 2 else p[1] + p[2]

#    def p_acc_clause_3(self, p):
#       """ acc_clause : PRIVATE LPAREN identifier_list RPAREN 
#                      | FIRSTPRIVATE LPAREN identifier_list RPAREN
#       """
#       p[0] = [acc_ast.AccClause(type = 'memory', name = str(p[1]).upper(), identifiers = p[3], coord = self._coord(p.lineno(1)))]
#
    def p_acc_clause_6(self, p):
       """ acc_clause : COLLAPSE LPAREN INT_CONST_DEC RPAREN """
       value = c_ast.Constant(value = p[3], type='int', coord = self._coord(p.lineno(1)))
       p[0] = [acc_ast.AccClause(type = str(p[1]) , name = str(p[1]).upper(), identifiers = value, coord = self._coord(p.lineno(1)))]

    def p_acc_clause_7(self, p):
        """ acc_clause : IF LPAREN expression RPAREN
        """
        p[0] = [acc_ast.AccClause(type = 'conditional', name = str(p[1]).upper(), identifiers = p[3], coord = self._coord(p.lineno(1)))]

    def p_acc_clause_8(self, p):
        """ acc_clause : ASYNC
                       | ASYNC LPAREN constant_expression RPAREN
        """
        val = c_ast.Constant(value = "0", type="int")
        if len(p) > 2:
            val = p[3]
        p[0] = [acc_ast.AccAsync(name = str(p[1]).upper(), value = val, coord = self._coord(p.lineno(1)))]

    def p_acc_clause_9(self, p):
        """ acc_clause : INDEPENDENT
        """
	# Nothing to do here
	p[0] = []



    def p_reduction_operator(self, p):
       """ reduction_operator  : PLUS 
                               | MINUS
                               | TIMES """
       p[0] = p[1]




# End of OpenAcc
####################


